import os
import shutil
from datetime import datetime
from typing import List, Callable, Optional
from .npm_manager import NpmManager

class BuildStage:
    """构建阶段类"""
    def __init__(self, name: str, func: Callable, *args, **kwargs):
        self.name = name
        self.func = func
        self.args = args
        self.kwargs = kwargs
        self.status = "pending"  # pending, running, success, failed
        self.output = ""
        self.start_time = None
        self.end_time = None

class BuildPipeline:
    """构建流水线类 - 类似Jenkins的阶段性构建"""
    
    def __init__(self, project_path: str, target_path: str):
        self.project_path = project_path
        self.target_path = target_path
        self.npm_manager = NpmManager(project_path)
        self.stages: List[BuildStage] = []
        self.logs: List[str] = []
        self.current_stage_index = -1  # 当前执行的阶段索引
        
        # 初始化构建阶段
        self._init_stages()
    
    def _init_stages(self):
        """初始化构建阶段"""
        self.stages = [
            BuildStage("检查环境", self._check_environment),
            BuildStage("检查项目文件", self._check_project_files),
            BuildStage("安装依赖", self._install_dependencies),
            BuildStage("构建项目", self._build_project),
            BuildStage("移动构建结果", self._move_build_output),
            BuildStage("清理工作区", self._cleanup_workspace)
        ]
    
    def _log(self, message: str):
        """记录日志"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_entry = f"[{timestamp}] {message}"
        self.logs.append(log_entry)
        print(log_entry)
    
    def _check_environment(self) -> tuple:
        """检查环境阶段"""
        self._log("开始检查Node.js和npm环境")
        success, message = self.npm_manager.check_environment()
        self._log(message)
        return success, message
    
    def _check_project_files(self) -> tuple:
        """检查项目文件阶段"""
        self._log(f"检查项目路径: {self.project_path}")
        
        # 检查项目路径是否存在
        if not os.path.exists(self.project_path):
            return False, f"项目路径不存在: {self.project_path}"
        
        # 检查是否存在package.json
        package_json_path = os.path.join(self.project_path, "package.json")
        if not os.path.exists(package_json_path):
            return False, f"项目中未找到package.json: {package_json_path}"
        
        self._log("项目文件检查通过")
        return True, "项目文件检查通过"
    
    def _install_dependencies(self) -> tuple:
        """安装依赖阶段"""
        self._log("开始安装项目依赖")
        success, message = self.npm_manager.install_dependencies()
        self._log(message)
        return success, message
    
    def _build_project(self) -> tuple:
        """构建项目阶段"""
        self._log("开始构建项目")
        success, message = self.npm_manager.build_project()
        self._log(message)
        return success, message
    
    def _build_project_to_target(self) -> tuple:
        """直接构建项目到目标目录阶段"""
        self._log(f"开始直接构建项目到目标目录: {self.target_path}")
        success, message = self.npm_manager.build_project_to_target(self.target_path)
        self._log(message)
        return success, message
    
    def _check_build_output_at_target(self) -> tuple:
        """检查目标目录中的构建输出阶段"""
        self._log(f"检查目标目录中的构建输出: {self.target_path}")
        if not os.path.exists(self.target_path):
            return False, f"目标构建目录不存在: {self.target_path}"
        
        # 检查目录是否为空
        if not os.listdir(self.target_path):
            return False, f"目标构建目录为空: {self.target_path}"
        
        self._log("目标目录中的构建输出检查通过")
        return True, f"构建输出已生成在: {self.target_path}"
    
    def _move_build_output(self) -> tuple:
        """移动构建结果阶段"""
        self._log(f"移动构建结果到目标路径: {self.target_path}")
        success, message = self.npm_manager.move_build_output(self.target_path)
        self._log(message)
        return success, message
    
    def _cleanup_workspace(self) -> tuple:
        """清理工作区阶段"""
        try:
            self._log(f"删除源文件: {self.project_path}")
            # 使用专门处理只读文件的删除函数
            self._rmtree_with_retry(self.project_path)
            self._log("源文件删除成功")
            return True, "工作区清理完成"
        except Exception as e:
            self._log(f"警告: 删除源文件时出错: {str(e)}")
            return True, f"工作区清理完成（部分文件未删除）: {str(e)}"
    
    def _rmtree_with_retry(self, path):
        """带重试机制的目录删除函数，专门处理只读文件和长路径问题"""
        import stat
        def remove_readonly(func, path, _):
            """清除只读属性并重试删除"""
            try:
                os.chmod(path, stat.S_IWRITE)
                func(path)
            except Exception:
                # 如果chmod失败，尝试直接删除
                try:
                    func(path)
                except:
                    pass
        
        # 尝试多次删除，因为Windows上文件可能被锁定
        for i in range(5):  # 增加重试次数
            try:
                if os.path.exists(path):
                    shutil.rmtree(path, onerror=remove_readonly)
                return
            except Exception as e:
                if i == 4:  # 最后一次尝试
                    # 记录警告但不中断流程
                    print(f"警告: 无法完全删除目录 {path}: {str(e)}")
                    return
                import time
                time.sleep(1 << i)  # 指数退避: 1s, 2s, 4s, 8s
    
    def get_next_stage(self) -> Optional[BuildStage]:
        """
        获取下一个待执行的阶段
        
        Returns:
            BuildStage: 下一个阶段，如果没有则返回None
        """
        if self.current_stage_index + 1 < len(self.stages):
            return self.stages[self.current_stage_index + 1]
        return None
    
    def execute_next_stage(self) -> tuple:
        """
        执行下一个阶段
        
        Returns:
            tuple: (stage_info, success, message, logs)
        """
        next_stage = self.get_next_stage()
        if not next_stage:
            return None, False, "所有阶段已执行完成", self.logs
        
        self.current_stage_index += 1
        stage = self.stages[self.current_stage_index]
        
        stage.status = "running"
        stage.start_time = datetime.now()
        
        try:
            success, message = stage.func(*stage.args, **stage.kwargs)
            stage.output = message
            
            stage.end_time = datetime.now()
            
            if success:
                stage.status = "success"
            else:
                stage.status = "failed"
                self._log(f"阶段 '{stage.name}' 失败: {message}")
                
            # 构造阶段信息
            stage_info = {
                "index": self.current_stage_index,
                "name": stage.name,
                "status": stage.status,
                "output": stage.output,
                "start_time": stage.start_time.isoformat() if stage.start_time else None,
                "end_time": stage.end_time.isoformat() if stage.end_time else None,
                "next_stage": self.get_next_stage().name if self.get_next_stage() else None
            }
            
            return stage_info, success, message, self.logs
            
        except Exception as e:
            stage.status = "failed"
            stage.end_time = datetime.now()
            error_msg = f"阶段 '{stage.name}' 发生未预期错误: {str(e)}"
            self._log(error_msg)
            
            # 构造阶段信息
            stage_info = {
                "index": self.current_stage_index,
                "name": stage.name,
                "status": stage.status,
                "output": error_msg,
                "start_time": stage.start_time.isoformat() if stage.start_time else None,
                "end_time": stage.end_time.isoformat() if stage.end_time else None,
                "next_stage": self.get_next_stage().name if self.get_next_stage() else None
            }
            
            return stage_info, False, error_msg, self.logs
    
    def get_stage_status(self) -> List[dict]:
        """获取各阶段状态"""
        return [
            {
                "index": i,
                "name": stage.name,
                "status": stage.status,
                "output": stage.output,
                "start_time": stage.start_time.isoformat() if stage.start_time else None,
                "end_time": stage.end_time.isoformat() if stage.end_time else None
            }
            for i, stage in enumerate(self.stages)
        ]
    
    def is_completed(self) -> bool:
        """检查流水线是否已完成"""
        return self.current_stage_index >= len(self.stages) - 1